var Endpoint        =   require("../util/endpoint");
var HashRing        =   require("hashring");
var AdapterProxy    =   require("./adapterProxy");

var EndpointManager =   function(RefObjectProxy, ObjectName, options) {
    options = options || {};
    this._objName       =   ObjectName;
    this._worker        =   RefObjectProxy;
    this._adapters      =   [];
    this._hash          =   0;
    this._iVnodeNumber  = 100;             //一致性哈希，虚拟节点个数
    this._hashRing      = undefined;        //一致性hashring

    if(options.bEnableConsistentHash){
        this._hashRing = new HashRing();
        if(typeof options.vnodeNumber == "number") this._iVnodeNumber = options.vnodeNumber;
    }

    this._initialize(ObjectName);
}
module.exports  =   EndpointManager;

EndpointManager.prototype._initialize = function(ObjectProxyName) {
    var options = ObjectProxyName.split('@');
    if(options.length != 2) {
        return;
    }
    this._objName   =   options[0];
    var endpoints    =   options[1].split(":");
    for(var i = 0; i < endpoints.length; i++) {
        this._addEndpoint(Endpoint.parse(endpoints[i]));
    }
}

EndpointManager.prototype.getAllAdapters    =   function() {
    return this._adapters || [];
}

EndpointManager.prototype._initHashRing =   function() {
    this._hashRing  = new HashRing();
    var hasKey, hash;
    for(var i = 0; i < this._adapters.length; i++) {
        var endpoint    =   this._adapters[i].endpoint;
        hasKey  =   endpoint.sHost + ":" + endpoint.iPort;
        hash = {};
        hash[hasKey] = {vnode: this._iVnodeNumber};
        this._hashRing.add(hash);
    }
}

EndpointManager.prototype._addEndpoint      =   function(endpoint) {
    var adapter =   new AdapterProxy();
    adapter._worker =   this._worker;
    adapter.endpoint    =   endpoint;
    adapter.initialize();

    this._adapters.push(adapter);

    if(this._hashRing) {
        var hasKey = endpoint.sHost + ":" + endpoint.iPort;
        var hash = {};
        hash[hasKey] = {vnode: this._iVnodeNumber};
        this._hashRing.add(hash);
    }

    return adapter;
}

EndpointManager.prototype._findEndpointByInfo   =   function(endpoint) {
    for(var i = 0; i < this._adapters.length; i++) {
        var adapter =   this._adapters[i];
        if(adapter._endpoint.sProtocol === endpoint.sProtocol && adapter._endpoint.sHost === endpoint.sHost && adapter._endpoint.iPort === endpoint.iPort) {
            return adapter;
        }
    }

    return undefined;
}

EndpointManager.prototype._findEndpointByIpPort = function(ipPort) {
    for(var i = 0; i < this._adapters.length; i++) {
        var adapter =   this._adapters[i];
        if(adapter._endpoint.sHost === ipPort.sHost && adapter._endpoint.iPort === ipPort.iPort) {
            return adapter;
        }
    }

    return undefined;
}

EndpointManager.prototype.selectAdapterProxy    =   function(reqMessage) {
    if(!this._adapters || this._adapters.length === 0) return;

    var adapter, adapters = [];
    var hashCode    =   +reqMessage.request.property['hashCode'];
    var consistentHash = reqMessage.request.property['consistentHash'];
    var useHashCode =   consistentHash || (hashCode && !isNaN(hashCode));
    adapters    =   this._adapters.slice(0);
    if(useHashCode) {
        if(consistentHash) {
            if(!this._hashRing) {
                this._initHashRing();
            }
            var range = this._hashRing.range(consistentHash, adapters.length);
            var firstHashAdapter;
            do{
                var ipPort = range[0].split(":");
                adapter =   this._findEndpointByIpPort({sHost : ipPort[0], ipPort : parseInt(ipPort[1])});
                if(!adapter) {
                    range.splice(0,1);
                    continue;
                }
                if(adapter.checkActive()) {
                    return adapter;
                }
                range.splice(0,1);
                if(!firstHashAdapter) {
                    firstHashAdapter = adapter;
                }
            }while(range.length > 0);
            adapter =   firstHashAdapter;
        } else {
            var hash;
            do {
                hash    =   hashCode % adapters.length;
                adapter =   adapters[hash];
                if(adapter.checkActive()) {
                    return adapter;
                }
                adapters.splice(hash, 1);
            }while(adapters.length > 0)
            adapter =   this._adapters.slice[0];
            hash    =   hashCode %  adapters.length;
            adapter =   adapters[hash];
        }
        adapter.checkActive(true);
        reqMessage.adapter  =   adapter;
        reqMessage.RemoteEndpoint   =   adapter._endpoint;
        adapter.pushTimeoutQueueN(reqMessage);
        
        return;
    }

    for(var i = 0; i < this._adapters.length; i++) {
        adapter =   this._adapters[(this._hash++) % this._adapters.length];
        if(adapter.checkActive()) {
            return adapter;
        }
    }

    adapter =   this._adapters[(this._hash++) % this._adapters.length];
    adapter.checkActive(true);

    return;
}

EndpointManager.prototype.doFinishInvoke    =   function(endpoint, iResultCode) {
    var adapter =   this._findEndpointByInfo(endpoint);
    if(adapter) {
        adapter._doFinishInvoke(iResultCode);
    }
}

EndpointManager.prototype.destroy   =   function() {
    for(var i = 0; i < this._adapters.length; i++) {
        this._adapters[i].destroy();
    }
}